#!/usr/bin/env python
import pygame
from math import floor, sqrt, fabs
from Const import REGION_WIDTH, REGION_HEIGHT

class Physics_object:
    bounce = 0   #bouce factor for coll_ obj
    friction = 1 #friction factor for coll_ obj
    hspeed = 0
    extra_hspeed = 0
    vspeed = 0
    extra_vspeed = 0
    gravity = 1
    solid = False
    health = 100
    destroyable = True
    x = 0
    y = 0
    xprev = 0
    yprev = 0
    w = 1
    h = 1
    region = []
    region_prev = []
    offset = (0, 0)
    collbot = False
    collside = None
    coll_obj_bot = None
    perform_friction = False
    ai_end_left = True
    ai_end_right = True
    coll_only_solid = True
    death = False
    
    def init(self, level, x, y, image, bbox = None):
        self.x = x
        self.y = y
        self.image = image
        self.level = level
        if bbox != None:
            self.offset = (bbox[0], bbox[1])
            self.rect = (bbox[0] + x, bbox[1] + y, bbox[2], bbox[3])
        else:
            self.offset = (0, 0)
            self.rect = (x, y, image.get_width(), image.get_height())
            
        self.region = self.find_collision_region(level)
        self.font = pygame.font.SysFont("Arial", 28)
        self.text = self.font.render(str(self.region[0]), True, (255, 255, 255))
    
    def override_update(self, level, events):
        pass
    
    def collision_other(self, other):
        pass
    def collision_self(self, other):
        pass
    def update(self, level, events, fps):
        self.override_update(level, events)
        if self.extra_hspeed != 0:
            self.hspeed += self.extra_hspeed
        if self.extra_vspeed != 0:
            self.vspeed += self.extra_vspeed
        prev_extra_vspeed = self.extra_vspeed
        prev_extra_hspeed = self.extra_hspeed
        self.begin_update()
        self.vspeed += float(self.gravity)
        if self.hspeed != 0:
            coll_objs = []       
            bak_rect = self.rect
            bak_region = self.region[:]
            
            self.rect = (self.x + self.offset[0] + self.hspeed, self.y + self.offset[1], self.rect[2], self.rect[3])
            self.region = self.find_collision_region(level)
            for r in self.region:
                coll_objs += level.find_collision(self.rect, r, self, True, False)
            self.rect = bak_rect
            self.region = bak_region
            
            hadcolx = False
            for o in coll_objs:
                o.collision_other(self)
                if self.coll_only_solid:
                    if o.solid == False:
                        coll_objs.remove(o)
                        continue
                if o == self.coll_obj_bot:
                    continue
                if self.hspeed > 0:
                    self.x = o.rect[0] - self.rect[2] - self.offset[0]
                else:
                    self.x = o.rect[0] + o.rect[2] - self.offset[0]
                self.moved(level)
                hadcolx = True
                
            if hadcolx:
                self.hspeed = 0
                self.collside = coll_objs[0]
                self.collision_self(self.collside)
            else:
                self.collside = None
                self.x += self.hspeed
                self.moved(level)
            
        if self.vspeed != 0:
            coll_objs = []
            bak_rect = self.rect
            bak_region = self.region[:]
            self.rect = (self.x + self.offset[0], self.y + self.offset[1] + self.vspeed, self.rect[2], self.rect[3])
            self.region = self.find_collision_region(level)
            for r in self.region:
                coll_objs += level.find_collision((self.rect[0], self.rect[1] + self.vspeed,
                                                    self.rect[2], self.rect[3]), r, self, True, False)
            self.rect = bak_rect
            self.region = bak_region
                        
            hadcoly = False
            
            self.coll_obj_bot = None
            for o in coll_objs:
                o.collision_other(self)
                if o.solid == False:
                    coll_objs.remove(o)
                    continue
                if self.coll_obj_bot == None and len(coll_objs) > 0:
                    self.coll_obj_bot = coll_objs[0]
                if self.vspeed >= 0:
                    if o.rect[1] <= self.coll_obj_bot.rect[1]:
                        self.coll_obj_bot = o
                        continue
                else:
                    if o.rect[1] > self.coll_obj_bot.rect[1]:
                        self.coll_obj_bot = o
                        continue
                if o.rect[1] == self.coll_obj_bot.rect[1]:
                    if self.hspeed >= 0:
                        if o.rect[0] >= self.coll_obj_bot.rect[0]:
                            self.coll_obj_bot = o
                            continue
                    else:
                        if o.rect[0] < self.coll_obj_bot.rect[0]:
                            self.coll_obj_bot = o
                            continue
                        
            if self.coll_obj_bot != None:            
                if self.vspeed > 0:
                    self.y = self.coll_obj_bot.rect[1] - self.rect[3] - self.offset[1]
                else:
                    self.y = self.coll_obj_bot.rect[1] + self.coll_obj_bot.rect[3] - self.offset[1]
                self.moved(level)
                hadcoly = True
                
            if hadcoly:
                self.collision_self(self.coll_obj_bot)
                if self.vspeed >= 0:
                    if not self.collbot:
                        self.collbot = True
                else:
                    if self.collbot:
                        self.collbot = False
                self.vspeed *= -self.coll_obj_bot.bounce
                if self.vspeed < 1 and self.vspeed > -1:
                    self.vspeed = 0
                if self.perform_friction:
                    self.hspeed *= sqrt(float(fabs(self.coll_obj_bot.friction - 1)))
                    self.hspeed += prev_extra_hspeed * self.coll_obj_bot.friction
                    
                if self.collbot:
                    self.extra_hspeed = self.coll_obj_bot.hspeed
                    self.extra_vspeed = self.coll_obj_bot.vspeed
                    if self.extra_hspeed != 0:
                        if prev_extra_hspeed == 0:                          
                            self.hspeed += self.extra_hspeed
                    if self.extra_vspeed != 0:
                        if prev_extra_vspeed == 0:                          
                            self.vspeed += self.extra_vspeed
                    
            else:
                self.y += self.vspeed
                if self.collbot:
                    self.collbot = False
                self.moved(level)
        self.region = self.find_collision_region(level)
        self.end_update(level)
    
    def begin_update(self):
        self.extra_hspeed = 0
        self.extra_vspeed = 0
        self.xprev = self.x
        self.yprev = self.y
        self.coll_objs = []
        self.coll_obj_bot_prev = self.coll_obj_bot
    
    def moved(self, level):
        self.rect = (self.x + self.offset[0], self.y + self.offset[1], self.rect[2], self.rect[3])
        self.region = self.find_collision_region(level)
    
    def hasmoved(self):
        if self.x != self.xprev:
            return 1
        elif self.y != self.yprev:
            return 1
        else: return 0
    
    def end_update(self, level):
        if self.death:
            return
        if self.extra_hspeed != 0:
            self.hspeed -= self.extra_hspeed
        self.vspeed -= self.extra_vspeed
        if self.region != self.region_prev:
            if len(self.region) > 0:
                self.text = self.font.render(str(self.region), True, (255, 255, 255))
            else:
                self.text.fill((255, 255, 255, 0))
            level.change_region(self, self.region_prev[:], self.region[:])
            self.region_prev = self.region[:]
    
    def find_collision_region(self, level):
        ret = []
        rx = int(floor(self.rect[0] / REGION_WIDTH))
        ry = int(floor(self.rect[1] / REGION_HEIGHT))
        if (rx >= 0 and rx < level.hor_regions and
            ry >= 0 and ry < level.vert_regions):
            ret.append(level.region_indices[rx][ry])
        
        rx = int(floor((self.rect[0] + self.rect[2]) / REGION_WIDTH))
        ry = int(floor(self.rect[1] / REGION_HEIGHT))
        if (rx >= 0 and rx < level.hor_regions and
            ry >= 0 and ry < level.vert_regions):            
            if not ret.__contains__(level.region_indices[rx][ry]):
                ret.append(level.region_indices[rx][ry])
            
        rx = int(floor(self.rect[0] / REGION_WIDTH))
        ry = int(floor((self.rect[1] + self.rect[3]) / REGION_HEIGHT))
        if (rx >= 0 and rx < level.hor_regions and
            ry >= 0 and ry < level.vert_regions):
            if not ret.__contains__(level.region_indices[rx][ry]):
                ret.append(level.region_indices[rx][ry])
            
        rx = int(floor((self.rect[0] + self.rect[2]) / REGION_WIDTH))
        ry = int(floor((self.rect[1] + self.rect[3]) / REGION_HEIGHT))
        if (rx >= 0 and rx < level.hor_regions and
            ry >= 0 and ry < level.vert_regions):
            if not ret.__contains__(level.region_indices[rx][ry]):
                ret.append(level.region_indices[rx][ry])
        ret.sort()
        return ret
    
    def get_new_instance(self, level, il):
        pass
    
    def bullet_hit(self, hit_pos):
        pass
    
    def hit(self, dammage, hit_pos):
        if self.destroyable:
            self.hspeed = 0
            self.health -= dammage
            if self.health <= 0:
                self.event_death()
                self.death = True
                self.level.remove(self, self.region)
        self.bullet_hit(hit_pos)
    
    def event_death(self):
        pass
    
    def override_back_draw(self, camera):
        pass
    
    def override_front_draw(self, camera):
        pass
    
    def draw(self, camera):
        self.override_back_draw(camera)
        camera.blit(self.image, (self.x, self.y))
        #camera.blit(self.text, (self.x, self.y))
        self.override_front_draw(camera)